#include "QRpc.h"

#include <QJsonObject>

QJsonRpc::QJsonRpc(const std::string & json)
    : _doc(QJsonDocument::fromJson(QByteArray::fromStdString(json)))
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(const QString & json)
    : _doc(QJsonDocument::fromJson(json.toUtf8()))
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(QJsonDocument doc)
    : _doc(doc)
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(QString method, QJsonValue params, int id)
    : _doc(QJsonObject {{
        {"jsonrpc", "2.0"},
        {"method", method},
        {"params", params},
        {"id", id},
    }})
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(QString method, int id)
    : _doc(QJsonObject {{
        {"jsonrpc", "2.0"},
        {"method", method},
        {"id", id},
    }})
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(QString method, QJsonValue params)
    : _doc(QJsonObject {{
        {"jsonrpc", "2.0"},
        {"method", method},
        {"params", params},
    }})
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

// QRpc::QRpc(QString method)
//     : _doc(QJsonObject {{
//         {"jsonrpc", "2.0"},
//         {"method", method},
//     }})
// {
//     setupMembers();
// }

QJsonRpc::QJsonRpc(QJsonValue result, int id)
    : _doc(QJsonObject {{
        {"jsonrpc", "2.0"},
        {"result", result},
        {"id", id},
    }})
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(int code, QString message, QJsonValue data, int id)
    : _doc(QJsonObject {{
        {"jsonrpc", "2.0"},
        {"error", QJsonObject {{
            {"code",    code},
            {"message", message},
            {"data",    data},
        }}},
        {"id", id},
    }})
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

QJsonRpc::QJsonRpc(int code, QString message, int id)
    : _doc(QJsonObject {{
        {"jsonrpc", "2.0"},
        {"error", QJsonObject {{
            {"code",    code},
            {"message", message},
        }}},
        {"id", id},
    }})
{
    if (_doc.isNull() || !_doc.isObject())
        return;

    setupMembers();
}

const QJsonValue & QJsonRpc::params() const
{
    return _params ? *_params : _fail;
}

const QJsonValue & QJsonRpc::result() const
{
    return _result ? *_result : _fail;
}

const QJsonValue & QJsonRpc::error() const
{
    return _error ? *_error : _fail;
}

void QJsonRpc::setupMembers()
{
    if (_is_valid)
        return;

    const auto object = _doc.object();
    const QJsonValue & jsonrpc_value = object["jsonrpc"];
    const QJsonValue & method_value  = object["method"];
    const QJsonValue & id_value      = object["id"];
    const QJsonValue & params_value  = object["params"];
    const QJsonValue & result_value  = object["result"];
    const QJsonValue & error_value   = object["error"];

    if (jsonrpc_value.isString())
        _jsonrpc_version = jsonrpc_value.toString();
    else
        _jsonrpc_version = "1.0";
    if (method_value.isString())
        _method = method_value.toString();
    if (id_value.isDouble())
        _id = id_value.toInt();
    if (params_value.isArray() || params_value.isObject())
        _params = &params_value;
    if (object.find("result") != object.end())
        _result = &result_value;
    if (error_value.isObject())
        _error = &error_value;

    if (!_method.isEmpty() && (_result || _error))
        return;

    if (_method.isEmpty() && !_result && !_error && _id < 0)
        return;

    if (_result && _error)
        return;

    if ((_result || _error) && _params)
        return;

    _is_valid = true;
}

